package com.photoeditor.demo.model.bean.stash;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.text.TextPaint;
import android.text.TextUtils;

import com.common.base.ComponentContext;
import com.common.base.utils.DeviceUtils;
import com.photoeditor.R;
import com.photoeditor.demo.model.image.collage.util.MathUtil;
import com.photoeditor.demo.model.image.util.ImageHelper;
import com.photoeditor.demo.ui.widget.stash.container.ContainerView;
import com.photoeditor.demo.util.HolidayUtil;

import java.util.Calendar;

import static com.photoeditor.demo.model.bean.EditEmojiBean.RADIUS;
import static com.photoeditor.demo.model.bean.stash.StickerBean.SMALLEST_SIZE;

/**
 * Created by Administrator on 2017/7/4 0004.
 */

public class DateStickerBean implements ContainerBean {

    public static final int TYPE_WEEK = 114;
    public static final int TYPE_DATE = 115;

    private Resources mResources = ComponentContext.getContext().getResources();

    private LinearGradient mLinearGradient;

    private TextPaint mTextPaint;
    private TextPaint mHolidayPaint;
    private Paint mBgPaint;

    private Matrix mMatrix;

    private RectF mRect;

    private RectF mButtonRect;
    private RectF mDeleteRect;

    private RectF mLeftOperationRect;
    private RectF mTopOperationRect;
    private RectF mRightOperationRect;
    private RectF mBottomOperationRect;

    private RectF mLastRectBeforeScale;

    private int mBgColor = Color.WHITE;
    private int mTextColor = Color.WHITE;

    private String mString;

    private float mDegree = 0;
    private float mRealDegree = 0;

    private float mVerticalPadding = DEFAULT_PADDING_VERTICAL;
    private float mHorizontalPadding = DEFAULT_PADDING_HORIZONTAL;

    private static final float DEFAULT_PADDING_VERTICAL = DeviceUtils.dip2px(ComponentContext.getContext(), 8);
    private static final float DEFAULT_PADDING_HORIZONTAL = DeviceUtils.dip2px(ComponentContext.getContext(), 10);

    public static final float MAX_WIDTH = ImageHelper.SCREEN_WIDTH * 1.2f;

    private static final float DEFAULT_TEXT_SIZE = DeviceUtils.sp2pxF(ComponentContext.getContext(), 36f);

    private static final float DEFAULT_TEXT_SIZE_MOTH = DeviceUtils.sp2pxF(ComponentContext.getContext(), 27f);
    private static final float DEFAULT_TEXT_SIZE_HOLIDAY = DeviceUtils.sp2pxF(ComponentContext.getContext(), 17f);

    private float mTextSize = DEFAULT_TEXT_SIZE;

    private Paint.FontMetrics mFontMetrics;
    private Paint.FontMetrics mFontMetricsHoliday;

    private Bitmap mTextBitmap;
    private Canvas mCanvas;

    private boolean mIsGradient = true;
    private String mHoliday = "";
    private Calendar mCalendar;

    private int mType;
    private Matrix mLastMatrix;

    private float mFlipX;
    private float mFlipY;

    public DateStickerBean(RectF mDrawableRect, int type){
        this(mDrawableRect, type, Calendar.getInstance());
    }

    public DateStickerBean(int type){
        this(new RectF(), type);
    }

    public DateStickerBean(RectF mDrawableRect, int type, Calendar calendar){
        this(mDrawableRect.centerX(), mDrawableRect.centerY(), type, calendar);
    }

    public DateStickerBean(float centerX, float centerY, int type, Calendar calendar){
        String s = "";
        mCalendar = calendar;
        mType = type;
        if(mType == TYPE_WEEK){
            mIsGradient = false;
            mBgColor = mResources.getColor(R.color.date_sticker_bg);
            s = getDay(mCalendar.get(Calendar.DAY_OF_WEEK)).toUpperCase();
        }else if(mType == TYPE_DATE){
            mIsGradient = true;
            mBgColor = mResources.getColor(R.color.white);
            s = getMonth(mCalendar.get(Calendar.MONTH)).toUpperCase() + " " + mCalendar.get(Calendar.DAY_OF_MONTH);
            mHoliday = new HolidayUtil().getHolidayToday().toUpperCase();
            if(!TextUtils.isEmpty(mHoliday)){
                mTextSize = DEFAULT_TEXT_SIZE_MOTH;
            }
        }

        mFlipX = 1;
        mFlipY = 1;

        initPaint();

        mString = s;
        mRect = new RectF();
        mButtonRect = new RectF();
        mDeleteRect = new RectF();
        mLeftOperationRect = new RectF();
        mTopOperationRect = new RectF();
        mRightOperationRect = new RectF();
        mBottomOperationRect = new RectF();
        mLastRectBeforeScale = new RectF();
        mMatrix = new Matrix();

        initData(centerX, centerY);
    }


    public DateStickerBean(int type, RectF rect, float degree, int dayOfWeek, int month, int dayOfMonth){
        String s = "";
        mType = type;
        if(mType == TYPE_WEEK){
            mIsGradient = false;
            mBgColor = mResources.getColor(R.color.date_sticker_bg);
            s = getDay(dayOfWeek).toUpperCase();
        }else if(mType == TYPE_DATE){
            mIsGradient = true;
            mBgColor = mResources.getColor(R.color.white);
            s = getMonth(month).toUpperCase() + " " + dayOfMonth;
            mHoliday = new HolidayUtil().getHolidayToday().toUpperCase();
            if(!TextUtils.isEmpty(mHoliday)){
                mTextSize = DEFAULT_TEXT_SIZE_MOTH;
            }
        }

        mFlipX = 1;
        mFlipY = 1;

        initPaint();

        mString = s;
        mRect = new RectF();
        mButtonRect = new RectF();
        mDeleteRect = new RectF();
        mLeftOperationRect = new RectF();
        mTopOperationRect = new RectF();
        mRightOperationRect = new RectF();
        mBottomOperationRect = new RectF();
        mLastRectBeforeScale = new RectF();
        mMatrix = new Matrix();

        float scale = rect.width() / (Math.max(mTextPaint.measureText(mString), mHolidayPaint.measureText(mHoliday))+mHorizontalPadding*2);

        mVerticalPadding = mVerticalPadding * scale;
        mHorizontalPadding = mHorizontalPadding * scale;
        mTextPaint.setTextSize(mTextPaint.getTextSize() * scale);
        mHolidayPaint.setTextSize(mHolidayPaint.getTextSize() * scale);

        mFontMetrics = mTextPaint.getFontMetrics();

        mDegree = degree;
        mRealDegree = mDegree;
        mRect.set(rect);
        countOtherRect();
        mLastRectBeforeScale.set(mRect);

        updateBitmap();
    }

    private void initData(float centerX, float centerY) {
        float width = 0, height = 0;

        width = Math.max(mTextPaint.measureText(mString), mHolidayPaint.measureText(mHoliday));

        mFontMetrics = mTextPaint.getFontMetrics();
        if(TextUtils.isEmpty(mHoliday)){
            height = mFontMetrics.descent - mFontMetrics.ascent;
        }else{
            mFontMetricsHoliday = mHolidayPaint.getFontMetrics();
            height = mFontMetrics.descent - mFontMetrics.ascent + mFontMetricsHoliday.descent - mFontMetricsHoliday.ascent ;
        }

        mRect.left = centerX - width / 2 - mHorizontalPadding;
        mRect.top = centerY - height / 2 - mHorizontalPadding;
        mRect.right = mRect.left + width + 2 * mVerticalPadding;
        mRect.bottom = mRect.top + height + 2 * mVerticalPadding;
        countOtherRect();
        mLastRectBeforeScale.set(mRect);

        updateBitmap();
    }

    void initPaint(){
        mTextPaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
        mTextPaint.setAntiAlias(true);
        mTextPaint.setTextSize(mTextSize);
        mTextPaint.setTextAlign(Paint.Align.CENTER);
        mTextPaint.setFakeBoldText(true);

        mHolidayPaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
        mHolidayPaint.setAntiAlias(true);
        mHolidayPaint.setTextSize(DEFAULT_TEXT_SIZE_HOLIDAY);
        mHolidayPaint.setTextAlign(Paint.Align.CENTER);
        mHolidayPaint.setFakeBoldText(true);

        mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mBgPaint.setAntiAlias(true);
        mBgPaint.setStyle(Paint.Style.FILL);
    }


    @Override
    public void setIsTouch(boolean isTouch) {

    }

    @Override
    public boolean isTouch() {
        return false;
    }

    @Override
    public Matrix getMatrix() {
        mMatrix.setRotate(-mDegree, mRect.centerX(), mRect.centerY());
        return mMatrix;
    }

    @Override
    public RectF getRect() {
        return mRect;
    }

    @Override
    public void setDegree(float degree) {
        mRealDegree += degree;
        float remain = mRealDegree % 90;
        if(Math.abs(remain) >= 0 && Math.abs(remain) < 3) {
            this.mDegree = mRealDegree - remain;
        } else if(Math.abs(remain) > 87) {
            if (remain > 0) {
                this.mDegree = mRealDegree + 90 - remain;
            } else {
                this.mDegree = mRealDegree - 90 - remain;
            }
        } else {
            mDegree = mRealDegree;
        }
    }

    @Override
    public float getDegree() {
        return mDegree;
    }

    @Override
    public void dealScaleAndRotation(float downX, float downY, float nowX, float nowY) {
        float px = mRect.centerX();
        float py = mRect.centerY();

        float x1 = downX - px;
        float y1 = downY - py;

        float x2 = nowX - px;
        float y2 = nowY - py;

        float scale = (float) (Math.sqrt(x2 * x2 + y2 * y2) / Math.sqrt(x1 * x1 + y1 * y1));

        double degree1 = MathUtil.getDegree(px, py, downX, downY, px + 1, py);

        double degree2 = MathUtil.getDegree(px, py, nowX, nowY, px + 1, py);

        int location1 = MathUtil.getLocation(x1, y1);
        int location2 = MathUtil.getLocation(x2, y2);

        /**
         * 这里是以px, py作为原点   如果是一二象限则 是逆时针旋转  取负值
         *
         * 如果是三四象限则顺时针角度比较小 取正值
         */
        if (location2 == 1 || location2 == 2) {
            degree2 = -degree2;
        }

        if (location1 == 1 || location1 == 2) {
            degree1 = -degree1;
        }

        double degree = degree2 - degree1;

        setScale(scale);
        setDegree((float) degree);
    }

    @Override
    public void setScale(float scale) {
        Matrix m = new Matrix();
        m.setScale(scale, scale, mRect.centerX(), mRect.centerY());
        RectF rect = new RectF();
        m.mapRect(rect, mRect);

        if (rect.width() >= SMALLEST_SIZE && rect.width() <= MAX_WIDTH) {
            mVerticalPadding = mVerticalPadding * scale;
            mHorizontalPadding = mHorizontalPadding * scale;
            mTextPaint.setTextSize(mTextPaint.getTextSize() * scale);
            mHolidayPaint.setTextSize(mHolidayPaint.getTextSize() * scale);

            float width = 0, height;
            width = Math.max(mTextPaint.measureText(mString), mHolidayPaint.measureText(mHoliday));

            mFontMetrics = mTextPaint.getFontMetrics();
            if(TextUtils.isEmpty(mHoliday)){
                height = mFontMetrics.descent - mFontMetrics.ascent;
            }else{
                mFontMetricsHoliday = mHolidayPaint.getFontMetrics();
                height = mFontMetrics.descent - mFontMetrics.ascent + mFontMetricsHoliday.descent - mFontMetricsHoliday.ascent ;
            }

            float centerX = rect.centerX();
            float centerY = rect.centerY();

            rect.left = centerX - width / 2 - mHorizontalPadding;
            rect.right = rect.left + width + 2 * mHorizontalPadding;
            rect.top = centerY - height / 2 - mVerticalPadding;
            rect.bottom = rect.top + height + 2 * mVerticalPadding;

            mRect = rect;
            countOtherRect();
            mLastRectBeforeScale.set(mRect);

            updateBitmap();
        }
    }

    private void updateBitmap(){
        if(mTextBitmap != null) {
            mTextBitmap.recycle();
        }
        mTextBitmap = Bitmap.createBitmap((int) mRect.width(), (int) mRect.height(), Bitmap.Config.ARGB_8888);
        mCanvas = new Canvas(mTextBitmap);
        mBgPaint.setColor(mBgColor);
        mCanvas.drawRoundRect(new RectF(0, 0, mRect.width(), mRect.height()), mRect.height()/6,mRect.height()/6, mBgPaint);
        if(mIsGradient) {
            mLinearGradient = new LinearGradient(0, 0, mRect.width(), 0,
                    new int[]{ComponentContext.getContext().getResources().getColor(R.color.date_sticker_start_color),
                            ComponentContext.getContext().getResources().getColor(R.color.date_sticker_end_color)},
                    null, Shader.TileMode.REPEAT);
            mTextPaint.setShader(mLinearGradient);
            mHolidayPaint.setShader(mLinearGradient);
        }else{
            mTextPaint.setColor(mTextColor);
        }
        if(TextUtils.isEmpty(mHoliday)){
            mCanvas.drawText(mString, mRect.width() / 2, mVerticalPadding + (mRect.height() - mVerticalPadding * 2) / 2 - mFontMetrics.top / 2 - mFontMetrics.bottom / 2, mTextPaint);
        }else {
            mCanvas.drawText(mString, mRect.width() / 2, mVerticalPadding + (mRect.height() - mVerticalPadding * 2) / 4 - mFontMetrics.top / 2 - mFontMetrics.bottom / 2, mTextPaint);
            if (!TextUtils.isEmpty(mHoliday)) {
                mCanvas.drawText(mHoliday, mRect.width() / 2, mVerticalPadding + (mRect.height() - mVerticalPadding * 2) * 3 / 4 - mFontMetricsHoliday.top / 2 - mFontMetricsHoliday.bottom / 2, mHolidayPaint);
            }
        }

    }

    @Override
    public void move(float dx, float dy) {
        mRect.offset(dx, dy);
        mButtonRect.offset(dx, dy);
        mDeleteRect.offset(dx, dy);
        mLeftOperationRect.offset(dx, dy);
        mRightOperationRect.offset(dx, dy);
        mTopOperationRect.offset(dx, dy);
        mBottomOperationRect.offset(dx, dy);
        mLastRectBeforeScale.offset(dx, dy);
    }

    @Override
    public void moveTo(RectF lastRectF, float dx, float dy) {
        mRect.left = lastRectF.left - dx;
        mRect.right = lastRectF.right - dx;
        mRect.top = lastRectF.top - dy;
        mRect.bottom = lastRectF.bottom - dy;
        countOtherRect();
    }

    @Override
    public void scaleTo(RectF lastRectF, float scale) {
        Matrix m = new Matrix();
        m.setScale(scale, scale, mRect.centerX(), mRect.centerY());
        m.mapRect(mRect, lastRectF);
        countOtherRect();
    }

    @Override
    public void setLastMatrix(Matrix matrix) {
        if (mLastMatrix == null) {
            mLastMatrix = new Matrix();
        }
        mLastMatrix.set(matrix);
    }

    @Override
    public void doScaleMove(RectF baseRect, RectF supperRect, Matrix matrix, Matrix cacheMatrix, RectF viewRect) {
        if (mLastMatrix == null) {
            RectF dstRect = new RectF();
            RectF a = new RectF(mRect);
            a.offset(-viewRect.left, -viewRect.top);

            //防止坐标对他的影响
            RectF supperRect1 = new RectF(supperRect);
            supperRect1.offset(-viewRect.left, -viewRect.top);
            //aScale是 baseRect -> supperRect的缩放值
            float bScale = supperRect1.width() / baseRect.width();
            Matrix m = new Matrix();
            m.postScale(bScale, bScale, baseRect.centerX(), baseRect.centerY());
            m.postTranslate(supperRect1.centerX() - baseRect.centerX(), supperRect1.centerY() - baseRect.centerY());
            m.invert(m);

            //matrixA是去掉 从baseRect转换到supperRect后的Matrix
            Matrix matrixA = new Matrix(matrix);
            matrixA.preConcat(m);
            matrixA.mapRect(dstRect, a);

            float scale = dstRect.width() / a.width();
            float dx = dstRect.centerX() - a.centerX();
            float dy = dstRect.centerY() - a.centerY();

            cacheMatrix.reset();
            cacheMatrix.setScale(scale, scale, a.centerX(), a.centerY());
            cacheMatrix.mapRect(dstRect, a);

            dstRect.offset(viewRect.left, viewRect.top);
            dstRect.offset(dx, dy);

            if (mRect.width() >= SMALLEST_SIZE && mRect.width() <= MAX_WIDTH) {
                mRect = dstRect;
                countOtherRect();
            }

            mLastMatrix = new Matrix();
            mLastMatrix.set(matrix);
        } else {
            RectF dstRect = new RectF();
            RectF a = new RectF(mRect);
            a.offset(-viewRect.left, -viewRect.top);

            mLastMatrix.invert(mLastMatrix);
            //matrixA是去掉 从baseRect转换到supperRect后的Matrix
            Matrix matrixA = new Matrix(matrix);
            matrixA.preConcat(mLastMatrix);
            matrixA.mapRect(dstRect, a);

            float scale = dstRect.width() / a.width();
            float dx = dstRect.centerX() - a.centerX();
            float dy = dstRect.centerY() - a.centerY();

            cacheMatrix.reset();
            cacheMatrix.setScale(scale, scale, a.centerX(), a.centerY());
            cacheMatrix.mapRect(dstRect, a);

            dstRect.offset(viewRect.left, viewRect.top);
            dstRect.offset(dx, dy);

            if (mRect.width() >= SMALLEST_SIZE && mRect.width() <= MAX_WIDTH) {
                mRect = dstRect;
                countOtherRect();
            }
            mLastMatrix.set(matrix);
        }
    }

    @Override
    public void setDegreeDirect(float degree) {
        this.mDegree = degree;
        mRealDegree = mDegree;
    }

    @Override
    public void setFlip(boolean horizontal, float value) {
        if(horizontal){
            mFlipX = value;
        } else {
            mFlipY = value;
        }
    }

    @Override
    public float getFlipX() {
        return mFlipX;
    }

    @Override
    public float getFlipY() {
        return mFlipY;
    }

    @Override
    public int getType() {
        return ContainerView.BEAN_TYPE_DATE;
    }

    @Override
    public RectF getLastRectBeforeScale() {
        return mLastRectBeforeScale;
    }

    @Override
    public Bitmap getBitmap() {
        return mTextBitmap;
    }

    @Override
    public void onDestroy() {
        if(mTextBitmap != null){
            mTextBitmap.recycle();
        }
    }

    public int getARType() {
        return mType;
    }

    public int getDayOfWeek(){
        if(mCalendar != null){
            return mCalendar.get(Calendar.DAY_OF_WEEK);
        }
        return 0;
    }

    public int getMonth(){
        if(mCalendar != null){
            return mCalendar.get(Calendar.MONTH);
        }
        return 0;
    }

    public int getDayOfMonth(){
        if(mCalendar != null){
            return mCalendar.get(Calendar.DAY_OF_MONTH);
        }
        return 0;
    }

    @Override
    public RectF getButtonRect() {
        return mButtonRect;
    }

    @Override
    public RectF getSettingRect() {
        return null;
    }

    @Override
    public RectF getDeleteRect() {
        return mDeleteRect;
    }

    @Override
    public RectF getLeftOperationRect() {
        return mLeftOperationRect;
    }

    @Override
    public RectF getTopOperationRect() {
        return mTopOperationRect;
    }

    @Override
    public RectF getRightOperationRect() {
        return mRightOperationRect;
    }

    @Override
    public RectF getBottomOperationRect() {
        return mBottomOperationRect;
    }

    @Override
    public void operationLeft(float dx) {

    }

    @Override
    public void operationRight(float dx) {

    }

    @Override
    public void operationTop(float dy) {

    }

    @Override
    public void operationBottom(float dy) {

    }

    private void countOtherRect() {
        mButtonRect.left = mRect.right - RADIUS;
        mButtonRect.top = mRect.bottom - RADIUS;
        mButtonRect.right = mRect.right + RADIUS;
        mButtonRect.bottom = mRect.bottom + RADIUS;

        mDeleteRect.left = mRect.left - RADIUS;
        mDeleteRect.top = mRect.top - RADIUS;
        mDeleteRect.right = mRect.left + RADIUS;
        mDeleteRect.bottom = mRect.top + RADIUS;
    }

    public void setBgColor(int bgColor) {
        mBgColor = bgColor;
    }

    public void setGradient(boolean gradient) {
        mIsGradient = gradient;
    }

    private String getDay(int day) {
        switch (day) {
            case 1:
                return mResources.getString(R.string.date_sunday);
            case 2:
                return mResources.getString(R.string.date_monday);
            case 3:
                return mResources.getString(R.string.date_tuesday);
            case 4:
                return mResources.getString(R.string.date_wednesday);
            case 5:
                return mResources.getString(R.string.date_thursday);
            case 6:
                return mResources.getString(R.string.date_friday);
            case 7:
                return mResources.getString(R.string.date_saturday);
            default:
                return mResources.getString(R.string.date_sunday);
        }
    }

    private String getMonth(int month) {
        switch (month) {
            case 0:
                return mResources.getString(R.string.date_january);
            case 1:
                return mResources.getString(R.string.date_february);
            case 2:
                return mResources.getString(R.string.date_march);
            case 3:
                return mResources.getString(R.string.date_april);
            case 4:
                return mResources.getString(R.string.date_may);
            case 5:
                return mResources.getString(R.string.date_june);
            case 6:
                return mResources.getString(R.string.date_july);
            case 7:
                return mResources.getString(R.string.date_august);
            case 8:
                return mResources.getString(R.string.date_september);
            case 9:
                return mResources.getString(R.string.date_october);
            case 10:
                return mResources.getString(R.string.date_november);
            case 11:
                return mResources.getString(R.string.date_december);
            default:
                return mResources.getString(R.string.date_december);
        }
    }

    public DateStickerBean deepClone(RectF parentRect) {
       return new DateStickerBean(parentRect, mType, mCalendar);
    }

    public DateStickerBean deepClone(float centerX, float centerY) {
        return new DateStickerBean(centerX, centerY, mType, mCalendar);
    }
}
